/*
 * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.lang;

/**
 * 包私有工具类，包含管理虚拟机关闭序列的数据结构和逻辑。
 *
 * @author   Mark Reinhold
 * @since    1.3
 *
 * @see java.io.Console
 * @see ApplicationShutdownHooks
 * @see java.io.DeleteOnExitHook
 */

class Shutdown {

    // 系统关闭钩子使用预定义槽注册。
    // 关闭钩子列表如下：
    // (0) 控制台恢复钩子
    // (1) ApplicationShutdownHooks，调用所有已注册的应用程序
    //     关闭钩子并等待它们完成
    // (2) DeleteOnExit钩子
    private static final int MAX_SYSTEM_HOOKS = 10;
    private static final Runnable[] hooks = new Runnable[MAX_SYSTEM_HOOKS];

    // 当前运行的关闭钩子在钩子数组中的索引
    private static int currentRunningHook = -1;

    // 跟踪我们是否已经（开始）关闭
    private static boolean isShutdown;

    /* 前面的静态字段由此锁保护 */
    private static class Lock { };
    private static Object lock = new Lock();

    /* 本地halt方法的锁对象 */
    private static Object haltLock = new Lock();

    /**
     * 添加新的系统关闭钩子。检查关闭状态和
     * 钩子本身，但不进行任何安全检查。
     *
     * registerShutdownInProgress参数应该为false，除了
     * 注册DeleteOnExitHook，因为第一个文件可能
     * 被应用程序关闭钩子添加到退出时删除列表中。
     *
     * @param slot  关闭钩子数组中的槽，其元素
     *              将在关闭期间按顺序调用
     * @param registerShutdownInProgress 为true时允许钩子
     *              即使关闭正在进行也能注册。
     * @param hook  要注册的钩子
     *
     * @throws IllegalStateException
     *         如果registerShutdownInProgress为false且关闭正在进行；或者
     *         如果registerShutdownInProgress为true且关闭过程
     *         已经通过了给定槽
     */
    static void add(int slot, boolean registerShutdownInProgress, Runnable hook) {
        if (slot < 0 || slot >= MAX_SYSTEM_HOOKS) {
            throw new IllegalArgumentException("无效槽: " + slot);
        }
        synchronized (lock) {
            if (hooks[slot] != null)
                throw new InternalError("槽 " + slot + " 处的关闭钩子已注册");

            if (!registerShutdownInProgress) {
                if (currentRunningHook >= 0)
                    throw new IllegalStateException("关闭正在进行中");
            } else {
                if (isShutdown || slot <= currentRunningHook)
                    throw new IllegalStateException("关闭正在进行中");
            }

            hooks[slot] = hook;
        }
    }

    /* 运行所有系统关闭钩子。
     *
     * 系统关闭钩子在同步于Shutdown.class的线程中运行。
     * 其他调用Runtime::exit、Runtime::halt
     * 或JNI DestroyJavaVM的线程将无限期阻塞。
     *
     * ApplicationShutdownHooks注册为单个钩子，启动
     * 所有应用程序关闭钩子并等待它们完成。
     */
    private static void runHooks() {
        synchronized (lock) {
            /* 防止守护线程在DestroyJavaVM启动关闭序列后
             * 调用exit的可能性
             */
            if (isShutdown) return;
        }

        for (int i=0; i < MAX_SYSTEM_HOOKS; i++) {
            try {
                Runnable hook;
                synchronized (lock) {
                    // 获取锁以确保在关闭期间注册的钩子
                    // 在这里可见。
                    currentRunningHook = i;
                    hook = hooks[i];
                }
                if (hook != null) hook.run();
            } catch (Throwable t) {
                if (t instanceof ThreadDeath) {
                    ThreadDeath td = (ThreadDeath)t;
                    throw td;
                }
            }
        }

        // 设置关闭状态
        // 同步是为了可见性；只有一个线程
        // 能够到达这里。
        synchronized (lock) {
            isShutdown = true;
        }
    }

    /* 通知VM是时候停止了。 */
    static native void beforeHalt();

    /* halt方法在halt锁上同步
     * 以避免关闭时删除文件列表的损坏。
     * 它调用真正的本地halt方法。
     */
    static void halt(int status) {
        synchronized (haltLock) {
            halt0(status);
        }
    }

    static native void halt0(int status);

    /* 由Runtime.exit调用，它执行所有安全检查。
     * 也由系统提供的终止事件的处理程序调用，
     * 应该传递非零状态码。
     */
    static void exit(int status) {
        synchronized (lock) {
            if (status != 0 && isShutdown) {
                /* 在非零状态时立即停止 */
                halt(status);
            }
        }
        synchronized (Shutdown.class) {
            /* 在类对象上同步，导致任何其他尝试
             * 启动关闭的线程无限期停滞
             */
            beforeHalt();
            runHooks();
            halt(status);
        }
    }


    /* 当最后一个非守护线程完成时由JNI DestroyJavaVM过程调用。
     * 与exit方法不同，此方法实际上不会停止VM。
     */
    static void shutdown() {
        synchronized (Shutdown.class) {
            runHooks();
        }
    }

}
